#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <ctype.h>
#include <unistd.h> // 系统调用接口所需头文件
#include <string.h> // strlen所需头文件
#include <sys/types.h> 
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>

#define MAX_LENGTH 1024 // 设置缓冲区的最大长度
#define ENTER_REDIRECT 0 // 输入重定向 标记为0
#define OUTPUT_REDIRECT 1 // 输出重定向 标记为1
#define APPEND_REDIRECT 2 // 追加重定向 标记为2
#define REDPID_DEFAULT 666 // 默认的重定向标记符 只要不是0 1 2 都行
char hostname[MAX_LENGTH]; // 存储主机名的字符数组
char pwd[MAX_LENGTH]; // 存储当前工作路径的字符数组
char command[MAX_LENGTH]; // 存储命令行参数
char* argv[MAX_LENGTH]; // 存储分割后的命令行参数
extern char** environ;
int exitNum; // 子进程的退出码
char* myenv[MAX_LENGTH]; // 存储用户自定义的环境变量
int myenvPos = 0; // 维护myenv数组
char* redirectname = NULL; // 记录重定向的文件名称
int redpid = REDPID_DEFAULT; // 标记是输入重定向 0 | 输出重定向 1 | 追加重定向 3 

char* slove(char* cmd)
{
    int fd = 0;
    char* pos = cmd;
    while(*pos)
    {
        if (*pos == '>')
        {
            if (*(pos + 1) == '>') // 追加重定向
            {
                *pos++ = '\0';
                *pos++ = '\0';
                while (isspace(*pos)) // 判断是否为空格
                {
                    pos++;
                }
                if (*pos) // 不为\0
                {
                    redpid = APPEND_REDIRECT;
                    return pos;
                }
                else 
                {
                    return NULL;
                }
            }
            else // 输出重定向
            {
                *pos++ = '\0';
                while (isspace(*pos))
                {
                    pos++;
                }
                
                if (*pos)
                {
                    redpid = OUTPUT_REDIRECT;
                    return pos;
                }
                else 
                {
                    return NULL;
                }
            }
        }
        else if(*pos == '<') // 输入重定向
        {
            *pos++ = '\0';
            while (isspace(*pos))
            {
                pos++;
            }
            if (*pos)
            {
                redpid = ENTER_REDIRECT;
                return pos;
            }
            else 
            {
                return NULL;
            }
        }
        else 
        {
            // do nothing
            ;
        }
        pos++;
    }
    return NULL;
}

int main()
{
    int hostnameRes = gethostname(hostname, MAX_LENGTH);
    // 检查gethostname函数的返回值
    if (hostnameRes == -1)
    {
        perror("gethostname");
    }

    char* cwdRes = getcwd(pwd, MAX_LENGTH);
    // 检查getcwd函数的返回值
    if (cwdRes == NULL)
    {
        perror("getcwd");
    }
    
    while(1)
    {
        printf("[%s@%s %s]$ ", getlogin(),hostname, pwd);
   		char* fgetsRes = fgets(command, sizeof(command), stdin);
    	// 验证fgets函数的返回值
    	if (fgetsRes == NULL)
    	{
        	perror("fgets");
    	}

        // 将读取到的回车置为'\0'
        // 注意：strlen将'\n'也算进去了，遇到'\0'就停止
        size_t realSize = strlen(command);
        command[realSize - 1] = '\0';
       
        // 重定向处理
        redirectname = slove(command);  

        // 分割命令行参数
        int i = 0;
        // echo "hello world" > log.txt
        argv[i++] = strtok(command, " ");  // 第一次调用，传入待分割的字符串
        while (argv[i++] = strtok(NULL, " ")); // 后续调用，第一个参数传入 NULLL
        int argc = i - 1;
        if (argc == 0)
        {
            // 用户一个字符都没输入的情况
            continue;
        }
      
        // 内建命令
        // cd
        if (argc == 2 && strcmp(argv[0], "cd") == 0)
        {    
            if (strcmp(argv[1], "~") == 0)
            {
                chdir(getenv("HOME"));
            }
            else if (strcmp(argv[1], "-") == 0)
            {
                chdir(getenv("OLDPWD"));
            }
            else 
            {
                chdir(argv[1]); // 改变当前的工作目录 
                // 以下需要更新环境变量PWD
                // 1. 更新pwd数组 
                char* getcwdRes = getcwd(pwd, MAX_LENGTH);
                if (getcwdRes == NULL) 
                {
                    perror("getcwd");
                }
                // 2. 替换 
                sprintf(getenv("PWD"), "%s", pwd);
            }
            continue;
        }
        
        // export
        if (argc == 2 && strcmp(argv[0], "export") == 0)
        {
            myenv[myenvPos] = malloc(strlen(argv[1]) + 1); // +1 是为了存储字符串结束符 '\0'
            if (myenv[myenvPos] == NULL) 
            {
                perror("malloc");
            }
            strcpy(myenv[myenvPos], argv[1]);
            putenv(myenv[myenvPos]);
            myenvPos++;
            continue;
        }

        // echo
        if (argc == 2 && strcmp(argv[0], "echo") == 0)
        {
            // 重定向 如 echo "xxx" >> log.txt
            int fd = 0;
            if (redirectname && redpid == ENTER_REDIRECT) // 输入重定向
            {
                fd = open(redirectname, O_RDONLY);
                if (fd == -1)
                {
                    perror("open");
                }
                char content[1024];
                memset(content, '\0', sizeof(content));
                ssize_t readRes = read(fd, content, strlen(content));
                if (readRes == -1)
                {
                    perror("read");
                }
                else 
                {
                    printf("%s\n", content);
                }
            }

            if (strcmp(argv[1], "$?") == 0)
            {
                // 退出码exitNum我定义在全局了
                printf("%d\n", exitNum);
                exitNum = 0; 
            }
            else if (*argv[1] == '$')
            {
                char* ans = getenv(argv[1] + 1);
                if (ans) printf("%s\n", ans);
            }

            //else if (*argv[1] == '\"' && argv[1][strlen(argv[1]) - 1] == '\"')
            else if (*argv[1] == '\"')
            {
                int size = strlen(argv[1]);
                char* last = argv[1];
                last = last + size - 1;
                if (*last == '\"')
                {
                    *last = '\0';
                }
                // 用户可能是这样输入的echo "hello >/>> xxx
                else 
                {
                    goto end;
                }
                if (redirectname && redpid == OUTPUT_REDIRECT) // 输出重定向
                {
                    fd = open(redirectname, O_WRONLY | O_CREAT | O_TRUNC, 0666);
                    if (fd == -1)
                    {
                        perror("open");
                    }
                    *last = '\n';
                    ssize_t writeRes = write(fd, argv[1] + 1, strlen(argv[1] + 1));
                    if (writeRes == -1)
                    {
                        perror("write");
                    }
                    close(fd);
                }
                else if (redirectname && redpid == APPEND_REDIRECT) // 追加重定向
                {
                    fd = open(redirectname, O_WRONLY | O_CREAT | O_APPEND, 0666);
                    if (fd == -1)
                    {
                        perror("open");
                    }
                    *last = '\n';
                    ssize_t writeRes = write(fd, argv[1] + 1, strlen(argv[1] + 1));
                    if (writeRes == -1)
                    {
                        perror("write");
                    }
                    close(fd);
                }
                else
                {
                    printf("%s\n", argv[1] + 1);
                }
            }
            else // echo xxxx
            {
end:
                if (redirectname && redpid == OUTPUT_REDIRECT) // 输出重定向
                {
                    fd = open(redirectname, O_WRONLY | O_CREAT | O_TRUNC, 0666);
                    if (fd == -1)
                    {
                        perror("open");
                    }
                    argv[1][strlen(argv[1])] = '\n';
                    ssize_t writeRes = write(fd, argv[1], strlen(argv[1]));
                    if (writeRes == -1)
                    {
                        perror("write");
                    }
                    close(fd);
                }
                else if (redirectname && redpid == APPEND_REDIRECT) // 追加重定向
                {
                    fd = open(redirectname, O_WRONLY | O_CREAT | O_APPEND, 0666);
                    if (fd == -1)
                    {
                        perror("open");
                    }
                    argv[1][strlen(argv[1])] = '\n';
                    ssize_t writeRes = write(fd, argv[1], strlen(argv[1]));
                    if (writeRes == -1)
                    {
                        perror("write");
                    }
                    close(fd);
                }
                else 
                {
                    printf("%s\n", argv[1]);
                }
            }
            continue;
        }
        
        // 程序替换 - 外部命令
        pid_t pid = fork();
        if (pid < 0)
        {
            // 说明子进程创建失败
            perror("fork");
        }
        else if (pid == 0)
        {
            int fd = 0;
            if (redirectname && redpid == ENTER_REDIRECT) // 输入重定向
            {
                fd = open(redirectname, O_RDONLY);
                dup2(fd, 0);
                redpid = REDPID_DEFAULT;
            }
            else if (redirectname && redpid == OUTPUT_REDIRECT) // 输出重定向
            {
                fd = open(redirectname, O_WRONLY | O_CREAT | O_TRUNC, 0666);
                dup2(fd, 1);
                redpid = REDPID_DEFAULT;
            }
            else if (redirectname && redpid == APPEND_REDIRECT) // 追加重定向
            {
                fd = open(redirectname, O_WRONLY | O_CREAT | O_APPEND, 0666);
                dup2(fd , 1);
                redpid = REDPID_DEFAULT;
            }

            if (fd == -1)
            {
                perror("open");
            }
            
            // ls高亮显示
            if (strcmp(argv[0], "ls") == 0)
            {
                argv[argc++] = "--color=auto";
            }
            // ll显示问题
            if (strcmp(argv[0], "ll") == 0)
            {
                argv[0] = "ls";
                argv[argc++] = "-l";
                argv[argc++] = "--color=auto";
            }
            // 子进程完成进程替换来执行外部命令
            execvpe(argv[0], argv, environ); 
            // 如果execvpe函数执行成功，后面的代码不会执行
            // 如果执行失败，我们自定义将其退出码设置为4
            exit(4);
        }
        else 
        {
            // 父进程负责回收子进程
            int status = 0;
            pid_t waitpidRes = waitpid(pid, &status, 0);

            if (waitpidRes == pid) // 如果返回值和子进程PID相等，说明回收成功
            {
                // 获取子进程的退出码
                exitNum = WEXITSTATUS(status);

            }
        }
        continue;
    }

    return 0;
}
